Audit statis atas 29 repo di ~/AppData/Local/hermes/repos, 7 Nov 2026.
Metode: pembacaan kode (rg/grep), bukan uji serang ke sistem berjalan.
| Aspek | Nilai | Catatan |
|---|---|---|
| Isolasi tenant & hashing password | B | scoped query + bcrypt/argon2 di app utama |
| Manajemen rahasia | D | token di URL git, keystore di repo, secret hardcoded |
| Ketahanan brute force | E | tidak ada rate limit di satu pun API |
| Header keamanan web | E | tanpa helmet/CSP/HSTS |
| Penanganan gagal-konfigurasi | D | fail-open bila env kosong |
| Keseluruhan | Cโ | fondasi ada, rahasia bocor |
1. Password Gitea di URL remote semua repo โ http://<user>:<token>@103.93.129.94:3001/...
tersimpan di .git/config tiap repo. Bocor satu = kendali seluruh 50 repo.
2. **Keystore + password signing pajak-kb-admin ter-commit** โ keystore.properties
memuat storePassword/keyPassword polos. Siapa pun bisa menerbitkan APK yang
diakui sebagai pembaruan resmi app itu.
3. Secret cadangan hardcoded dan masih diterima โ z-ecosystem-admin-2026 di
zone-repo/src/lib/secrets.ts, zxroom/lib/auth.ts, zx-carwash-api/src/routes/{admin,sso}.js.
4. Fail-open โ memberkit-api membuka seluruh API bila DEVICE_TOKEN kosong;
Z-Rooms/lib/jwt.ts memakai 'fallback-secret' bila NEXTAUTH_SECRET hilang.
bcrypt/argon2: zx-carwash-api, acumedis-api, zone-repo, zposcrypto.timingSafeEqual untuk membandingkan secret lintas-appaddHook('preHandler', requireAuth) global di zx-carwash-api.env tidak pernah ter-commit (hanya .env.example)| # | Langkah | Dampak | Estimasi |
|---|---|---|---|
| 1 | Rotasi token Gitea, pindah ke credential helper | kendali repo | 30 mnt |
| 2 | Keluarkan keystore dari git, ganti password | tanda tangan APK | 1 jam |
| 3 | Hapus OLD_SECRET dari 4 berkas | admin lintas-app | 30 mnt |
| 4 | Fail-closed bila env wajib kosong | akses tak berizin | 15 mnt |
| 5 | Rate limit login 5/menit per IP+email | brute force | 1 jam |
| 6 | Helmet + CSP + HSTS | clickjacking/downgrade | 1 jam |
Langkah 1โ4 adalah rahasia yang sudah bocor: memperbaiki kode tidak cukup,
nilainya harus diganti.
| Proyek | Jenis | Risiko utama |
|---|---|---|
| zone-repo (Z One) | Next.js + Prisma | OLD_SECRET hardcoded di src/lib/secrets.ts |
| zxroom / Z-Rooms | Next.js | OLD_SECRET hardcoded + fallback-secret di lib/jwt.ts |
| zx-carwash-api | Fastify | OLD_SECRET di routes/admin.js + routes/sso.js |
| zx-carwash (app) | Android | outbox retry tanpa batas (terlindung clientId) |
| memberkit-api | Node polos | fail-open: DEVICE_TOKEN kosong = API terbuka |
| pajak-kb-api | Next.js | JWT tanpa rotasi kunci, cron pakai JWT_SECRET yang sama |
| pajak-kb-admin | Android | keystore + password di repo |
| acumedis-api | Fastify | hashing benar, tanpa rate limit |
| ytvault-api | Python | token statis bersama, bukan per-pengguna |
| zpos, zxgym, Zadv, zomet-main | Next.js | tanpa CSP/HSTS |
Seluruh 29 repo memakai remote yang memuat token pribadi di URL.
git remote get-url origin โ http://djvpri:<token>@103.93.129.94:3001/djvpri/<repo>.git
git remote get-url origin
โ http://djvpri:<token>@103.93.129.94:3001/djvpri/<repo>.git
Ada di `.git/config` **setiap** repo. Token ini bisa membuat, menghapus, dan menimpa seluruh repo โ termasuk memaksa auto-deploy Coolify berjalan. **Tindakan:** cabut token lama di Gitea, buat token baru, simpan lewat credential helper (`git config --global credential.helper manager`), ubah remote menjadi `http://103.93.129.94:3001/djvpri/<repo>.git` tanpa user:token. ### 2. Keystore `pajak-kb-admin` di dalam git
Ada di .git/config setiap repo. Token ini bisa membuat, menghapus, dan
menimpa seluruh repo โ termasuk memaksa auto-deploy Coolify berjalan.
Tindakan: cabut token lama di Gitea, buat token baru, simpan lewat
credential helper (git config --global credential.helper manager), ubah remote
menjadi http://103.93.129.94:3001/djvpri/<repo>.git tanpa user:token.
git show HEAD:keystore.properties storePassword=<password polos> keyAlias=pajakkbadmin keyPassword=<password polos>
git show HEAD:keystore.properties
storePassword=<password polos>
keyAlias=pajakkbadmin
keyPassword=<password polos>
**Tindakan:** keluarkan `keystore.properties` + `app/keystore/release.jks` dari repo (`git rm --cached`), tambahkan ke `.gitignore`, pindahkan JKS ke `C:/Users/Anjuv/keystores/`, ganti password keystore, lalu terbitkan APK baru dengan kunci itu. Catat: APK lama tetap sah sampai pengguna memasang versi baru. ### 3. `z-ecosystem-admin-2026` Diterima sebagai secret cadangan di:
Tindakan: keluarkan keystore.properties + app/keystore/release.jks dari
repo (git rm --cached), tambahkan ke .gitignore, pindahkan JKS ke
C:/Users/Anjuv/keystores/, ganti password keystore, lalu terbitkan APK baru
dengan kunci itu. Catat: APK lama tetap sah sampai pengguna memasang versi baru.
Diterima sebagai secret cadangan di:
zone-repo/src/lib/secrets.ts:8 zxroom/lib/auth.ts:36 Z-Rooms/lib/auth.ts:36 zx-carwash-api/src/routes/admin.js:29 zx-carwash-api/src/routes/sso.js:36
zone-repo/src/lib/secrets.ts:8
zxroom/lib/auth.ts:36
Z-Rooms/lib/auth.ts:36
zx-carwash-api/src/routes/admin.js:29
zx-carwash-api/src/routes/sso.js:36
**Tindakan:** hapus semua baris `OLD_SECRET`, sisakan satu sumber (`CROSS_APP_SECRET` dari env). Bila env kosong โ **tolak start**, jangan diam. ### 4. Fail-open
Tindakan: hapus semua baris OLD_SECRET, sisakan satu sumber
(CROSS_APP_SECRET dari env). Bila env kosong โ tolak start, jangan diam.
memberkit-api/server.js:7 const TOKEN = process.env.DEVICE_TOKEN || '' memberkit-api/server.js:93 if (TOKEN && ...) return 401 โ env kosong = lolos Z-Rooms/lib/jwt.ts:6 process.env.NEXTAUTH_SECRET || 'fallback-secret'
memberkit-api/server.js:7 const TOKEN = process.env.DEVICE_TOKEN || ''
memberkit-api/server.js:93 if (TOKEN && ...) return 401 โ env kosong = lolos
Z-Rooms/lib/jwt.ts:6 process.env.NEXTAUTH_SECRET || 'fallback-secret'
**Tindakan:** ganti menjadi gagal saat start bila env wajib tidak ada.
Tindakan: ganti menjadi gagal saat start bila env wajib tidak ada.
zx-carwash-api 0 pembatas acumedis-api 0 memberkit-api 0 ytvault-api 0 pajak-kb-api 0
zx-carwash-api 0 pembatas
acumedis-api 0
memberkit-api 0
ytvault-api 0
pajak-kb-api 0
Endpoint login bisa dicoba ribuan kali tanpa hambatan; endpoint yang memanggil Gemini/yt-dlp bisa dipakai menghabiskan kuota Anda. **Konsep:** pembatas per `IP + email`, 5 percobaan/menit, jeda bertambah setelah 10 kegagalan. Simpan di memori proses (satu instance per app โ cukup), tanpa Redis. ## Belum ada header keamanan
Endpoint login bisa dicoba ribuan kali tanpa hambatan; endpoint yang memanggil
Gemini/yt-dlp bisa dipakai menghabiskan kuota Anda.
Konsep: pembatas per IP + email, 5 percobaan/menit, jeda bertambah setelah
10 kegagalan. Simpan di memori proses (satu instance per app โ cukup), tanpa Redis.
helmet / CSP / X-Frame-Options / HSTS โ 0 hasil di 9 proyek web
helmet / CSP / X-Frame-Options / HSTS โ 0 hasil di 9 proyek web
**Konsep:** satu middleware bersama. `X-Frame-Options: DENY` untuk halaman aplikasi, `frame-ancestors 'none'`, `Strict-Transport-Security: max-age=31536000`, dan CSP longgar dulu (laporkan pelanggaran, baru perketat) agar tidak mematahkan halaman yang ada.
Konsep: satu middleware bersama. X-Frame-Options: DENY untuk halaman
aplikasi, frame-ancestors 'none', Strict-Transport-Security: max-age=31536000,
dan CSP longgar dulu (laporkan pelanggaran, baru perketat) agar tidak mematahkan
halaman yang ada.
ytvault-api memakai satu API_TOKEN statis untuk semua pemasangan. Bocor dari
satu APK = siapa pun bisa memakai server Anda untuk mengunduh.
Konsep: token per pemasangan (dibuat saat pertama kali app dijalankan),
disimpan server, bisa dicabut satu per satu di Setelan.
Semua findMany di zx-carwash-api sudah ber-tenantId, tetapi **tidak ada
constraint di DB** yang memaksa. Satu rute baru yang lupa menyaring = data
lintas tenant terbaca.
Konsep: bungkus akses Prisma dalam helper db.tenant(tenantId) yang selalu
menyisipkan filter โ bukan mengandalkan ingatan penulis kode.
pajak-kb-api/src/app/api/cron/expire/route.ts memakai JWT_SECRET sebagai
header cron. Bocor satu = bisa memicu job apa pun.
Konsep: kunci terpisah CRON_SECRET, hanya untuk cron, tanpa hak membuat sesi.
git remote -v > remote-lama.txt).keystore dan password baru di tempat aman.| # | Langkah | Verifikasi |
|---|---|---|
| 1 | Cabut token Gitea lama, buat baru | token lama โ 401 |
| 2 | Ubah remote tanpa token + credential helper | git fetch jalan tanpa user:token di URL |
| 3 | Keluarkan JKS + keystore.properties dari git | git ls-files tidak memuatnya |
| 4 | Ganti password keystore, terbitkan APK baru | apksigner verify + pasang di HP |
| 5 | Hapus OLD_SECRET di 5 berkas, wajibkan env | app menolak start bila env kosong |
| 6 | Fail-closed memberkit-api & Z-Rooms | API menolak saat env kosong |
| # | Langkah | Verifikasi |
|---|---|---|
| 7 | Rate limit 5/menit per IP+email di 5 API | percobaan ke-6 โ 429 |
| 8 | Helmet + CSP lapor + HSTS di 9 app web | header terlihat di curl -I |
| 9 | Token cron terpisah (CRON_SECRET) | header lama โ 401 |
| 10 | Token YTVault per pemasangan | satu token dicabut, sisanya jalan |
db.tenant(tenantId) di zx-carwash-api, acumedis-api, memberkit-api.dan permintaan tenant A tidak melihat data tenant B.
1. Satu langkah per commit, satu verifikasi nyata per langkah.
2. Tidak ada perubahan DB/infra tanpa persetujuan.
3. Setiap langkah dicatat hasilnya di halaman ini.
rg untuk pola rahasia, hashing, rate limit, header, timing-safe compare.git ls-files untuk mencari berkas kredensial yang ter-commit.git remote get-url untuk melihat token di URL.preHandler/middleware untuk memastikan auth global.npm audit terpisah per repo).Skor di halaman ini adalah penilaian atas kode yang terbaca, bukan jaminan
bahwa sistem aman. Bagian yang tidak diperiksa di atas dapat mengubah gambaran.
Saya periksa hal-hal yang belum masuk audit pertama: port server, dependensi,
SSRF, dan isolasi runtime. Hasilnya menambah satu temuan kritis baru dan
satu masalah serius yang tidak terlihat dari kode.
ss -tlnp di VPS 0.0.0.0:22 sshd 0.0.0.0:5432 docker-proxy โ container nginx zpos 0.0.0.0:3001 gitea 0.0.0.0:8000 coolify ufw status โ inactive iptables -S INPUT โ -P INPUT ACCEPT
ss -tlnp di VPS
0.0.0.0:22 sshd
0.0.0.0:5432 docker-proxy โ container nginx zpos
0.0.0.0:3001 gitea
0.0.0.0:8000 coolify
ufw status โ inactive
iptables -S INPUT โ -P INPUT ACCEPT
**Artinya:** Coolify (panel yang bisa men-deploy apa pun), Gitea (sumber semua kode), dan SSH dapat dijangkau dari internet mana pun. Tidak ada satu pun aturan firewall. Satu password lemah = seluruh ekosistem jatuh. Port **5432** terbuka karena container proxy milik app `zpos` memetakan `5432 -> 0.0.0.0:5432`. Itu pemetaan yang tidak perlu: proxy Coolify seharusnya hanya mendengarkan 80/443. **Konsep perbaikan:** | # | Langkah | Verifikasi | |---|---|---| | 1 | `ufw default deny incoming` + `allow 22,80,443` | `nmap` dari luar: hanya 3 port | | 2 | Batasi panel: Coolify 8000 & Gitea 3001 hanya via SSH tunnel atau IP kantor | `curl` dari luar โ connection refused | | 3 | Hapus pemetaan `5432:5432` pada container proxy zpos | `ss -tlnp` tidak memuat 5432 | | 4 | SSH: matikan login password, wajib kunci | `ssh -o PreferredAuthentications=password` โ ditolak | | 5 | fail2ban untuk SSH & Gitea | 5 gagal login โ IP diblokir 1 jam | Ini **tidak** memerlukan perubahan kode aplikasi sama sekali, dan dampaknya paling besar dibanding semua langkah lain. ### K2 โ Dependensi: 3 kerentanan kritis `next` + RCE di `next/og` Hasil `npm audit --omit=dev` per app: | App | Kritis | High | Yang paling berbahaya | |---|---|---|---| | zone-repo | 3 | 5 | `next` middleware bypass, `@auth/core` normalisasi email | | zxroom / Z-Rooms | 1 | 4 | **`next` RCE di `next/og` ImageResponse** | | zxgym | 2 | 3 | `@auth/core`, `next` DoS | | zpos | 1 | 6 | `next` middleware bypass, `xlsx` prototype pollution | | Zadv | 1 | 4 | `next` DoS | | zomet-main | 1 | 5 | `next` DoS, `js-yaml` ReDoS | | pajak-kb-api | 0 | 0 | 6 moderate | | acumedis-api | 0 | 2 | `fastify`/`find-my-way` DDoS | | zx-carwash-api | 0 | 2 | `find-my-way` DDoS | `s-next` versi lama dipakai di `zomet-main` (`^14.2.0`), `zxgym` (`14.2.35`). Dua app memakai `next@16.3.4` yang masih kena RCE `next/og`. **Konsep:** naikkan satu per satu dengan urutan risiko: `zxroom`, `Z-Rooms`, `zone-repo` (paling terekspos publik) lebih dulu. `npm audit fix` biasa dulu; `--force` hanya bila mayor, dan itu perlu uji halaman. ### K3 โ SSRF & pemakaian kuota di ytvault-api
Artinya: Coolify (panel yang bisa men-deploy apa pun), Gitea (sumber semua
kode), dan SSH dapat dijangkau dari internet mana pun. Tidak ada satu pun aturan
firewall. Satu password lemah = seluruh ekosistem jatuh.
Port 5432 terbuka karena container proxy milik app zpos memetakan
5432 -> 0.0.0.0:5432. Itu pemetaan yang tidak perlu: proxy Coolify seharusnya
hanya mendengarkan 80/443.
Konsep perbaikan:
| # | Langkah | Verifikasi |
|---|---|---|
| 1 | ufw default deny incoming + allow 22,80,443 | nmap dari luar: hanya 3 port |
| 2 | Batasi panel: Coolify 8000 & Gitea 3001 hanya via SSH tunnel atau IP kantor | curl dari luar โ connection refused |
| 3 | Hapus pemetaan 5432:5432 pada container proxy zpos | ss -tlnp tidak memuat 5432 |
| 4 | SSH: matikan login password, wajib kunci | ssh -o PreferredAuthentications=password โ ditolak |
| 5 | fail2ban untuk SSH & Gitea | 5 gagal login โ IP diblokir 1 jam |
Ini tidak memerlukan perubahan kode aplikasi sama sekali, dan dampaknya
paling besar dibanding semua langkah lain.
Hasil npm audit --omit=dev per app:
| App | Kritis | High | Yang paling berbahaya |
|---|---|---|---|
| zone-repo | 3 | 5 | next middleware bypass, @auth/core normalisasi email |
| zxroom / Z-Rooms | 1 | 4 | **next RCE di next/og ImageResponse** |
| zxgym | 2 | 3 | @auth/core, next DoS |
| zpos | 1 | 6 | next middleware bypass, xlsx prototype pollution |
| Zadv | 1 | 4 | next DoS |
| zomet-main | 1 | 5 | next DoS, js-yaml ReDoS |
| pajak-kb-api | 0 | 0 | 6 moderate |
| acumedis-api | 0 | 2 | fastify/find-my-way DDoS |
| zx-carwash-api | 0 | 2 | find-my-way DDoS |
s-next versi lama dipakai di zomet-main (^14.2.0), zxgym (14.2.35).
Dua app memakai next@16.3.4 yang masih kena RCE next/og.
Konsep: naikkan satu per satu dengan urutan risiko: zxroom, Z-Rooms,
zone-repo (paling terekspos publik) lebih dulu. npm audit fix biasa dulu;
--force hanya bila mayor, dan itu perlu uji halaman.
def info(request, url: str):
auth(request)
y.extract_info(url, download=False) # url dari klien, tanpa batasdef info(request, url: str):
auth(request)
y.extract_info(url, download=False) # url dari klien, tanpa batas
`url` diterima apa adanya. yt-dlp mengikuti URL itu; tanpa allowlist, server Anda bisa dipakai mengambil alamat internal (`http://127.0.0.1`, `10.0.1.x`, metadata cloud) atau menghabiskan kuota/disk. **Konsep:** tolak host selain `youtube.com`/`youtu.be`/`music.youtube.com`, batasi panjang URL, batasi 1 job per menit, dan bersihkan berkas job lama otomatis. ### K4 โ Token Gitea masih di dalam riwayat commit? Perlu diperiksa โ token di URL remote tidak masuk commit, tetapi `keystore.properties` **sudah** masuk. Mengeluarkannya dari HEAD tidak menghapusnya dari riwayat; siapa pun yang punya klon lama masih bisa membacanya. **Konsep:** anggap password keystore itu **sudah bocor** dan ganti. Untuk riwayat, cukup: repo Gitea bersifat privat dan hanya Anda yang punya akses, jadi penulisan ulang riwayat (`filter-repo`) tidak wajib โ tetapi password tetap harus diganti. ### Ringkasan prioritas baru
url diterima apa adanya. yt-dlp mengikuti URL itu; tanpa allowlist, server Anda
bisa dipakai mengambil alamat internal (http://127.0.0.1, 10.0.1.x, metadata
cloud) atau menghabiskan kuota/disk.
Konsep: tolak host selain youtube.com/youtu.be/music.youtube.com,
batasi panjang URL, batasi 1 job per menit, dan bersihkan berkas job lama
otomatis.
Perlu diperiksa โ token di URL remote tidak masuk commit, tetapi keystore.properties
sudah masuk. Mengeluarkannya dari HEAD tidak menghapusnya dari riwayat;
siapa pun yang punya klon lama masih bisa membacanya.
Konsep: anggap password keystore itu sudah bocor dan ganti. Untuk riwayat,
cukup: repo Gitea bersifat privat dan hanya Anda yang punya akses, jadi
penulisan ulang riwayat (filter-repo) tidak wajib โ tetapi password tetap
harus diganti.
P0 Firewall VPS + tutup 5432 + batasi panel (tanpa ubah kode, 30 menit) P1 Rotasi token Gitea & password keystore P2 Hapus OLD_SECRET + fail-closed P3 Rate limit + header keamanan P4 Naikkan dependensi (next, @auth/core, fastify) P5 SSRF allowlist + token per pemasangan
P0 Firewall VPS + tutup 5432 + batasi panel (tanpa ubah kode, 30 menit)
P1 Rotasi token Gitea & password keystore
P2 Hapus OLD_SECRET + fail-closed
P3 Rate limit + header keamanan
P4 Naikkan dependensi (next, @auth/core, fastify)
P5 SSRF allowlist + token per pemasangan
Saya awalnya melaporkan "backup tidak ada". Itu salah. Saya hanya membaca
crontab root dan melewatkan jadwal backup yang hidup di dalam Coolify.
Koreksi ini hasil pemeriksaan langsung ke Cloudflare R2, bukan pembacaan kode.
bucket zomet @ 0d2e...r2.cloudflarestorage.com (Cloudflare R2) objek R2 342 ยท kredensial masih sah (listObjectsV2 berhasil diuji) dump lokal /data/coolify/backups โ 3,4 GB riwayat 570 sukses ยท 4 gagal
bucket zomet @ 0d2e...r2.cloudflarestorage.com (Cloudflare R2)
objek R2 342 ยท kredensial masih sah (listObjectsV2 berhasil diuji)
dump lokal /data/coolify/backups โ 3,4 GB
riwayat 570 sukses ยท 4 gagal
| Database | Jadwal | Ke R2 | Terakhir | |---|---|---|---| | ZPos `1fhosrzrapewqtv4wkqiw1rs` | `0 */3 * * *` | โ | 10 Nov 06:01 | | Z One `jznfq1n9zkfdqtjcjwbcnxwb` | `0 20 * * *` | โ | 9 Nov 20:00 | | ZX Gym `wko4doynkswavl3gqibxchge` | `0 20 * * *` | โ | 9 Nov 20:00 | ### Yang MASIH BOLONG **Tidak punya jadwal backup sama sekali:** | Database | Dampak | |---|---| | **AcuMedis** `shlrypoiye8df6ijn99lnbii` | data pasien โ paling serius | | **ZX Carwash** `9zrumir0odqrm3zoaxkh56wg` | transaksi, bon, antrian, kas harian | | **ZXRoom** `adyf5mac2kjppwuoqebk7naf` | properti & penyewaan | | **pajak-kb-api** `znoe26p9` | data wajib pajak | | MemberKit `e006bspftf2mjdmpbisks2k9` | member & tenant | | ytvault | kecil, tapi tanpa salinan | **Di luar Postgres โ tidak ada mekanisme:** - Repo Gitea (bind mount `/root/gitea/data`) โ **semua kode sumber** - Konfigurasi Coolify (`.env`, compose, volume `coolify-db`) - `.env` semua app (rahasia produksi; bocor = harus rotasi) - Berkas unduhan YTVault (besar, bisa diunduh ulang) **Temuan sampingan:** dari 334 objek ZPos di R2, ~320 bukan dump database โ itu materi LLM Zadv yang tersimpan di jalur lama. Dump asli hanya ~14 terakhir. ### Rencana perbaikan Tidak perlu skrip baru maupun ubah kode. Mekanisme yang sama sudah terbukti jalan untuk ZPos tiap 3 jam; yang kurang hanya mengisi jadwal di panel Coolify. | # | Database | Frekuensi | Ke S3 | |---|---|---|---| | 1 | ZX Carwash | `0 */3 * * *` (3 jam) | โ | | 2 | AcuMedis | `0 20 * * *` | โ | | 3 | ZXRoom | `0 20 * * *` | โ | | 4 | pajak-kb-api | `0 20 * * *` | โ | | 5 | MemberKit | `0 20 * * *` | โ | | 6 | ytvault | `0 20 * * *` | โ | Setelah keenam jadwal ini hidup, keenamnya mengikuti jalur yang sudah terbukti (570 sukses) โ tidak ada komponen baru yang bisa gagal. **Tahap berikutnya** (belum dikerjakan, perlu keputusan terpisah): | # | Langkah | Alasan | |---|---|---| | 7 | `tar` harian repo Gitea โ R2 | semua kode sumber ada di situ | | 8 | Backup `.env` terenkripsi โ R2 | tanpa ini, DB saja tidak cukup untuk memulihkan app | | 9 | Uji restore satu DB ke container sementara | backup yang tak pernah diuji bukan backup | ### Aturan masa depan untuk database baru Setiap kali app baru dibuat, **jadwal backupnya dibuat di hari yang sama.** Dua kali sekarang DB baru lahir tanpa backup (ZX Carwash, AcuMedis). Lebih baik menjadikannya bagian dari daftar pembuatan app daripada mengandalkan ingatan. ### Status Menunggu perintah. Belum ada jadwal yang dibuat dan belum ada berkas yang diubah.
| Database | Jadwal | Ke R2 | Terakhir |
|---|---|---|---|
| ZPos 1fhosrzrapewqtv4wkqiw1rs | 0 */3 * * * | โ | 10 Nov 06:01 |
| Z One jznfq1n9zkfdqtjcjwbcnxwb | 0 20 * * * | โ | 9 Nov 20:00 |
| ZX Gym wko4doynkswavl3gqibxchge | 0 20 * * * | โ | 9 Nov 20:00 |
Tidak punya jadwal backup sama sekali:
| Database | Dampak |
|---|---|
| AcuMedis shlrypoiye8df6ijn99lnbii | data pasien โ paling serius |
| ZX Carwash 9zrumir0odqrm3zoaxkh56wg | transaksi, bon, antrian, kas harian |
| ZXRoom adyf5mac2kjppwuoqebk7naf | properti & penyewaan |
| pajak-kb-api znoe26p9 | data wajib pajak |
| MemberKit e006bspftf2mjdmpbisks2k9 | member & tenant |
| ytvault | kecil, tapi tanpa salinan |
Di luar Postgres โ tidak ada mekanisme:
/root/gitea/data) โ semua kode sumber.env, compose, volume coolify-db).env semua app (rahasia produksi; bocor = harus rotasi)Temuan sampingan: dari 334 objek ZPos di R2, ~320 bukan dump database โ
itu materi LLM Zadv yang tersimpan di jalur lama. Dump asli hanya ~14 terakhir.
Tidak perlu skrip baru maupun ubah kode. Mekanisme yang sama sudah terbukti
jalan untuk ZPos tiap 3 jam; yang kurang hanya mengisi jadwal di panel Coolify.
| # | Database | Frekuensi | Ke S3 |
|---|---|---|---|
| 1 | ZX Carwash | 0 */3 * * * (3 jam) | โ |
| 2 | AcuMedis | 0 20 * * * | โ |
| 3 | ZXRoom | 0 20 * * * | โ |
| 4 | pajak-kb-api | 0 20 * * * | โ |
| 5 | MemberKit | 0 20 * * * | โ |
| 6 | ytvault | 0 20 * * * | โ |
Setelah keenam jadwal ini hidup, keenamnya mengikuti jalur yang sudah terbukti
(570 sukses) โ tidak ada komponen baru yang bisa gagal.
Tahap berikutnya (belum dikerjakan, perlu keputusan terpisah):
| # | Langkah | Alasan |
|---|---|---|
| 7 | tar harian repo Gitea โ R2 | semua kode sumber ada di situ |
| 8 | Backup .env terenkripsi โ R2 | tanpa ini, DB saja tidak cukup untuk memulihkan app |
| 9 | Uji restore satu DB ke container sementara | backup yang tak pernah diuji bukan backup |
Setiap kali app baru dibuat, jadwal backupnya dibuat di hari yang sama.
Dua kali sekarang DB baru lahir tanpa backup (ZX Carwash, AcuMedis). Lebih baik
menjadikannya bagian dari daftar pembuatan app daripada mengandalkan ingatan.
Menunggu perintah. Belum ada jadwal yang dibuat dan belum ada berkas yang
diubah.